/**
 * \file IfxGtm_Pwm.c
 * \brief GTM PWM details
 *
 * \version iLLD_1_0_1_17_0_1
 * \copyright Copyright (c) 2023 Infineon Technologies AG. All rights reserved.
 *
 *
 *
 *                                 IMPORTANT NOTICE
 *
 * Use of this file is subject to the terms of use agreed between (i) you or
 * the company in which ordinary course of business you are acting and (ii)
 * Infineon Technologies AG or its licensees. If and as long as no such terms
 * of use are agreed, use of this file is subject to following:
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer, must
 * be included in all copies of the Software, in whole or in part, and all
 * derivative works of the Software, unless such copies or derivative works are
 * solely in the form of machine-executable object code generated by a source
 * language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 *
 *
 */

/******************************************************************************/
/*----------------------------------Includes----------------------------------*/
/******************************************************************************/

#include "IfxGtm_Pwm.h"

/******************************************************************************/
/*------------------------Inline Function Prototypes--------------------------*/
/******************************************************************************/

/** \brief Get pointer to DTM
 * \param clusterSFR Cluster SFR
 * \param subModule GTM Sub Module
 * \param channel PWM channel
 * \return Returns pointer to DTM if available for given configuration
 */
IFX_INLINE Ifx_GTM_CDTM_DTM *IfxGtm_Pwm_getDtmPtr(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel);

/** \brief Enable Synchronous update of compare registers from shadow registers of the specified channel
 * \param glbCtrl Global control register
 * \param upenMask UPEN Mask of channel
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_enableChannelSyncUpdate(volatile uint32 *glbCtrl, uint32 upenMask);

/** \brief Disable Synchronous update of compare registers from shadow registers of the specified channel
 * \param glbCtrl Global control register
 * \param upenMask UPEN Mask of channel
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_disableChannelSyncUpdate(volatile uint32 *glbCtrl, uint32 upenMask);

/** \brief Enable Synchronous update of all configured channels in AGC/TGC0/TGC1
 * \param pwm PWM handle
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_enableChannelsSyncUpdate(IfxGtm_Pwm *pwm);

/** \brief Disable Synchronous update of all configured channels in AGC/TGC0/TGC1
 * \param pwm PWM handle
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_disableChannelsSyncUpdate(IfxGtm_Pwm *pwm);

/** \brief Enable the (A)TOM channel to start the counter when trigger is received
 * \param clusterSFR Pointer to the Cluster object
 * \param subModule ATOM/TOM
 * \param channel Channel index
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_enableChannel(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel);

/** \brief Enables the (A)TOM channel PWM output
 * Note: Channel starts generating PWM after receiving trigger
 * \param clusterSFR Pointer to the Cluster object
 * \param subModule ATOM/TOM
 * \param channel Channel index
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_enableChannelOutput(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel);

/** \brief Sets the (A)TOM channel clock source
 * \param clusterSFR Pointer to the Cluster object
 * \param subModule ATOM/TOM
 * \param channel Channel index
 * \param clockSource Configured clock source
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_setClockSource(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel, IfxGtm_Pwm_ClockSource clockSource);

/** \brief Set counter to reset either on CM0 match or when trigger is received
 * \param clusterSFR Pointer to the Cluster object
 * \param subModule ATOM/TOM
 * \param channel Channel index
 * \param event Reset event
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_setCounterResetEvent(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel, IfxGtm_Pwm_ResetEvent event);

/** \brief Set counter mode according to PWM signal alignment
 * \param clusterSFR Pointer to the Cluster object
 * \param subModule ATOM/TOM
 * \param channel Channel index
 * \param alignment PWM signal alignment
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_setCounterMode(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel, IfxGtm_Pwm_Alignment alignment);

/** \brief Generate a trigger when CN0 = CM0
 * \param clusterSFR Pointer to the Cluster object
 * \param subModule ATOM/TOM
 * \param channel Channel index
 * \param enabled TRUE: Generate trigger
 * FALSE: Forward trigger
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_generateTrigger(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel, boolean enabled);

/** \brief Initializes pin for base/sync channel based on submodule
 * \param pwm PWM handle
 * \param pin Pin configuration
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_initPin(IfxGtm_Pwm *pwm, IfxGtm_Pwm_Pin *pin);

/** \brief Function to configure Time base register with default values to generate trigger
 * \param clusterSFR Pointer to the Cluster object
 * \param subModule ATOM/TOM
 * \param channel Channel index
 * \param compareValue Generate trigger when counter reaches this value
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_configureTimeBaseTrigger(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel, uint32 compareValue);

/** \brief Update base channel frequency with requested value at the end of period (synchronously)
 * \param pwm PWM handle
 * \param requestFrequency Requested frequency in Hz
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_updateBaseChannelFrequency(IfxGtm_Pwm *pwm, float32 requestFrequency);

/** \brief Update base channel frequency with requested value immediately (asynchronously)
 * \param pwm PWM handle
 * \param requestFrequency Requested frequency in Hz
 * \return None
 */
IFX_INLINE void IfxGtm_Pwm_updateBaseChannelFrequencyImmediate(IfxGtm_Pwm *pwm, float32 requestFrequency);

/******************************************************************************/
/*-----------------------Private Function Prototypes--------------------------*/
/******************************************************************************/

/** \brief Returns clock frequency in Hz of the clock source configured for Atom/Tom
 * \param pwm PWM handle
 * \param gtmSFR gtmSFR
 * \param clockSource Clock configuration
 * \return Clock frequency in Hz
 */
IFX_STATIC float32 IfxGtm_Pwm_getSubModuleClockFrequency(IfxGtm_Pwm *pwm, Ifx_GTM *gtmSFR, IfxGtm_Pwm_ClockSource clockSource);

/** \brief Returns clock frequency in Hz of the clock source configured for Dtm
 * \param pwm PWM handle
 * \param gtmSFR gtmSFR
 * \param clockSource Clock configuration
 * \return Clock frequency in Hz
 */
IFX_STATIC float32 IfxGtm_Pwm_getDtmClockFrequency(IfxGtm_Pwm *pwm, Ifx_GTM *gtmSFR, IfxGtm_Dtm_ClockSource clockSource);

/** \brief Initializes the base/sync channel with user configuration for PWM generation
 * \param pwm PWM handle
 * \param config Configuration structure
 * \param configIndex Index of channel to be initialized
 * \return None
 */
IFX_STATIC void IfxGtm_Pwm_initChannel(IfxGtm_Pwm *pwm, IfxGtm_Pwm_Config *config, IfxGtm_Pwm_SyncChannelIndex configIndex);

/** \brief Initializes interrupt for base/sync channel
 * \param pwm PWM handle
 * \param interrupt Interrupt configuration
 * \param channel Channel Handle
 * \return None
 */
IFX_STATIC void IfxGtm_Pwm_initInterrupts(IfxGtm_Pwm *pwm, IfxGtm_Pwm_InterruptConfig *interrupt, IfxGtm_Pwm_Channel *channel);

/** \brief Updates PWM handle with pointers and masks for TGC/AGC after channel configuration
 * This data is used dusring run time for fast access
 * \param pwm PWM handle
 * \return None
 */
IFX_STATIC void IfxGtm_Pwm_updateGlobalControlUnitData(IfxGtm_Pwm *pwm);

/******************************************************************************/
/*---------------------Inline Function Implementations------------------------*/
/******************************************************************************/

IFX_INLINE Ifx_GTM_CDTM_DTM *IfxGtm_Pwm_getDtmPtr(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel)
{
    Ifx_GTM_CDTM_DTM *dtmSFR;

    if (subModule == IfxGtm_Pwm_SubModule_atom)
    {
        /* ATOM 0..7 connects to DTM 4..5 */
        dtmSFR = &clusterSFR->CDTM->DTM[(uint8)((uint8)channel >> (uint8)2u) + 4u];
    }
    else    /* TOM */
    {
        /* TOM 0..7 connects to DTM 0..1 */
        dtmSFR = &clusterSFR->CDTM->DTM[(uint8)((uint8)channel >> (uint8)2u)];
    }

    return dtmSFR;
}


IFX_INLINE void IfxGtm_Pwm_enableChannelSyncUpdate(volatile uint32 *glbCtrl, uint32 upenMask)
{
    *glbCtrl = upenMask & (uint32)IFXGTM_PWM_UPEN_ENABLE_VALUE;
}


IFX_INLINE void IfxGtm_Pwm_disableChannelSyncUpdate(volatile uint32 *glbCtrl, uint32 upenMask)
{
    *glbCtrl = upenMask & (uint32)IFXGTM_PWM_UPEN_DISABLE_VALUE;
}


IFX_INLINE void IfxGtm_Pwm_enableChannelsSyncUpdate(IfxGtm_Pwm *pwm)
{
    IfxGtm_Pwm_GlobalControl *globalctrl = &pwm->globalControl;

    /* Enable UPEN in TGC0/1 or AGC if all channels are in one control unit */
    if (pwm->globalControl.reg1 == NULL_PTR)
    {
        *globalctrl->reg0 = pwm->globalControl.upenMask0 & (uint32)IFXGTM_PWM_UPEN_ENABLE_VALUE;
    }
    /* Enable UPEN in TGC0,1 if all channels not in one control unit */
    else
    {
        *globalctrl->reg0 = pwm->globalControl.upenMask0 & (uint32)IFXGTM_PWM_UPEN_ENABLE_VALUE;
        *globalctrl->reg1 = pwm->globalControl.upenMask1 & (uint32)IFXGTM_PWM_UPEN_ENABLE_VALUE;
    }
}


IFX_INLINE void IfxGtm_Pwm_disableChannelsSyncUpdate(IfxGtm_Pwm *pwm)
{
    IfxGtm_Pwm_GlobalControl *globalctrl = &pwm->globalControl;

    /* Disable UPEN in TGC0/1 or AGC if all channels are in one control unit */
    if (pwm->globalControl.reg1 == NULL_PTR)
    {
        *globalctrl->reg0 = pwm->globalControl.upenMask0 & (uint32)IFXGTM_PWM_UPEN_DISABLE_VALUE;
    }
    /* Disable UPEN in TGC0,1 if all channels not in one control unit */
    else
    {
        *globalctrl->reg0 = pwm->globalControl.upenMask0 & (uint32)IFXGTM_PWM_UPEN_DISABLE_VALUE;
        *globalctrl->reg1 = pwm->globalControl.upenMask1 & (uint32)IFXGTM_PWM_UPEN_DISABLE_VALUE;
    }
}


IFX_INLINE void IfxGtm_Pwm_enableChannel(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel)
{
    if (subModule == IfxGtm_Pwm_SubModule_atom)
    {
        IfxGtm_Atom_Agc_enableChannel(&clusterSFR->ATOM->AGC, (IfxGtm_Atom_Ch)channel, TRUE, FALSE);
    }
    else    /* TOM */
    {
        Ifx_GTM_TOM_TGC *tomTgc = IfxGtm_Tom_Ch_getTgcPointer(clusterSFR->TOM, IFXGTM_PWM_GET_TGC_INDEX(channel));
        IfxGtm_Tom_Tgc_enableChannel(tomTgc, (IfxGtm_Tom_Ch)channel, TRUE, FALSE);
    }
}


IFX_INLINE void IfxGtm_Pwm_enableChannelOutput(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel)
{
    if (subModule == IfxGtm_Pwm_SubModule_atom)
    {
        IfxGtm_Atom_Agc_enableChannelOutput(&clusterSFR->ATOM->AGC, (IfxGtm_Atom_Ch)channel, TRUE, FALSE);
    }
    else    /* TOM */
    {
        Ifx_GTM_TOM_TGC *tomTgc = IfxGtm_Tom_Ch_getTgcPointer(clusterSFR->TOM, IFXGTM_PWM_GET_TGC_INDEX(channel));
        IfxGtm_Tom_Tgc_enableChannelOutput(tomTgc, (IfxGtm_Tom_Ch)channel, TRUE, FALSE);
    }
}


IFX_INLINE void IfxGtm_Pwm_setClockSource(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel, IfxGtm_Pwm_ClockSource clockSource)
{
    if (subModule == IfxGtm_Pwm_SubModule_atom)
    {
        IfxGtm_Atom_Ch_setClockSource(clusterSFR->ATOM, (IfxGtm_Atom_Ch)channel, clockSource.atom);
    }
    else    /* TOM */
    {
        IfxGtm_Tom_Ch_setClockSource(clusterSFR->TOM, (IfxGtm_Tom_Ch)channel, (IfxGtm_Tom_Ch_ClkSrc)clockSource.tom);
    }
}


IFX_INLINE void IfxGtm_Pwm_setCounterResetEvent(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel, IfxGtm_Pwm_ResetEvent event)
{
    if (subModule == IfxGtm_Pwm_SubModule_atom)
    {
        IfxGtm_Atom_Ch_setResetSource(clusterSFR->ATOM, (IfxGtm_Atom_Ch)channel, (IfxGtm_Atom_Ch_ResetEvent)event);
    }
    else    /* TOM */
    {
        IfxGtm_Tom_Ch_setResetSource(clusterSFR->TOM, (IfxGtm_Tom_Ch)channel, (IfxGtm_Tom_Ch_ResetEvent)event);
    }
}


IFX_INLINE void IfxGtm_Pwm_setCounterMode(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel, IfxGtm_Pwm_Alignment alignment)
{
    /* Edge aligned: b00 Count always up
     * Center aligned: b01 Count up and down and update CM0/1 when counter reaches 0 */
    uint8 counterMode = (alignment == IfxGtm_Pwm_Alignment_center) ? 1u : 0u;

    if (subModule == IfxGtm_Pwm_SubModule_atom)
    {
        Ifx_GTM_ATOM_CH *atomCh = IfxGtm_Atom_Ch_getChannelPointer(clusterSFR->ATOM, (IfxGtm_Atom_Ch)channel);
        atomCh->CTRL.B.UDMODE = counterMode;
    }
    else    /* TOM */
    {
        Ifx_GTM_TOM_CH *tomCh = IfxGtm_Tom_Ch_getChannelPointer(clusterSFR->TOM, (IfxGtm_Tom_Ch)channel);
        tomCh->CTRL.B.UDMODE = counterMode;
    }
}


IFX_INLINE void IfxGtm_Pwm_generateTrigger(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel, boolean enabled)
{
    if (subModule == IfxGtm_Pwm_SubModule_atom)
    {
        IfxGtm_Atom_Ch_setTriggerOutput(clusterSFR->ATOM, (IfxGtm_Atom_Ch)channel, (IfxGtm_Atom_Ch_OutputTrigger)enabled);
    }
    else    /* TOM */
    {
        IfxGtm_Tom_Ch_setTriggerOutput(clusterSFR->TOM, (IfxGtm_Tom_Ch)channel, (IfxGtm_Tom_Ch_OutputTrigger)enabled);
    }
}


IFX_INLINE void IfxGtm_Pwm_initPin(IfxGtm_Pwm *pwm, IfxGtm_Pwm_Pin *pin)
{
    if (pwm->subModule == IfxGtm_Pwm_SubModule_atom)
    {
        IfxGtm_PinMap_setAtomTout(&pin->outputPin->atom, pin->outputMode, pin->padDriver);
    }
    else    /* TOM */
    {
        IfxGtm_PinMap_setTomTout(&pin->outputPin->tom, pin->outputMode, pin->padDriver);
    }
}


IFX_INLINE void IfxGtm_Pwm_configureTimeBaseTrigger(IfxGtm_Pwm_ClusterSFR *clusterSFR, IfxGtm_Pwm_SubModule subModule, IfxGtm_Pwm_SubModule_Ch channel, uint32 compareValue)
{
    Ifx_GTM_ATOM_AGC_ACT_TB actTbReg;

    actTbReg.B.ACT_TB  = compareValue;      /* Compare Value */
    actTbReg.B.TBU_SEL = 0x0u;              /* TBU_TS0 */
    actTbReg.B.TB_TRIG = 0x1u;              /* Trigger when compare value matches */

    if (subModule == IfxGtm_Pwm_SubModule_atom)
    {
        clusterSFR->ATOM->AGC.ACT_TB.U = actTbReg.U;
    }
    else
    {
        if (IFXGTM_PWM_GET_TGC_INDEX(channel) == 0)
        {
            clusterSFR->TOM->TGC0_ACT_TB.U = actTbReg.U;
        }
        else
        {
            clusterSFR->TOM->TGC1_ACT_TB.U = actTbReg.U;
        }
    }
}


IFX_INLINE void IfxGtm_Pwm_updateBaseChannelFrequency(IfxGtm_Pwm *pwm, float32 requestFrequency)
{
    IfxGtm_Pwm_Channel *channel = &pwm->channels[0];

    /* Calculate period ticks (round to nearest integer) */
    uint32              periodTicks = (uint32)((float32)((pwm->sourceFrequency / requestFrequency) + (float32)0.5f));

    /* Divide by 2 for center aligned and add 1 (to reach desired value) */
    if (pwm->alignment != IfxGtm_Pwm_Alignment_edge)
    {
        periodTicks = (periodTicks >> 1) + 1;
    }

    /* Copy to handle */
    pwm->periodTicks = periodTicks;
    pwm->frequency   = requestFrequency;

    /* Update shadow register */
    *channel->registers.SR0 = periodTicks;
}


IFX_INLINE void IfxGtm_Pwm_updateBaseChannelFrequencyImmediate(IfxGtm_Pwm *pwm, float32 requestFrequency)
{
    IfxGtm_Pwm_Channel *channel = &pwm->channels[0];

    /* Calculate period ticks (round to nearest integer) */
    uint32              periodTicks = (uint32)((float32)((pwm->sourceFrequency / requestFrequency) + (float32)0.5f));

    /* Divide by 2 for center aligned and add 1 (to reach desired value) */
    if (pwm->alignment != IfxGtm_Pwm_Alignment_edge)
    {
        periodTicks = (periodTicks >> 1) + 1;
    }

    /* Copy to handle */
    pwm->periodTicks = periodTicks;
    pwm->frequency   = requestFrequency;

    /* Update compare register */
    *channel->registers.SR0 = periodTicks;
    *channel->registers.CM0 = periodTicks;
}


/******************************************************************************/
/*-------------------------Function Implementations---------------------------*/
/******************************************************************************/

void IfxGtm_Pwm_initConfig(IfxGtm_Pwm_Config *config, Ifx_GTM *gtmSFR)
{
    IfxGtm_Pwm_Config defaultConfig = {
        .gtmSFR      = NULL_PTR,
        .cluster     = IfxGtm_Cluster_0,
        .subModule   = IfxGtm_Pwm_SubModule_atom,
        .alignment   = IfxGtm_Pwm_Alignment_center,
        .numChannels = 0,
        .channels    = NULL_PTR,
        .frequency   = IFXGTM_PWM_DEFAULT_FREQUENCY,
        {
            .atom          = IfxGtm_Cmu_Clk_0,
        },
        .dtmClockSource    = IfxGtm_Dtm_ClockSource_systemClock,
        .syncUpdateEnabled = TRUE,
        .syncStart         = TRUE
    };

    /* Default Configuration */
    *config = defaultConfig;

    /* Copy SFR */
    config->gtmSFR = gtmSFR;
}


void IfxGtm_Pwm_initChannelConfig(IfxGtm_Pwm_ChannelConfig *channelConfig)
{
    IfxGtm_Pwm_ChannelConfig defaultConfig = {
        .timerCh   = IfxGtm_Pwm_SubModule_Ch_0,
        .phase     = 0.0f,
        .duty      = 0.0f,
        .dtm       = NULL_PTR,
        .output    = NULL_PTR,
        .mscOut    = NULL_PTR,
        .interrupt = NULL_PTR,
    };

    /* Default Configuration */
    *channelConfig = defaultConfig;
}


void IfxGtm_Pwm_init(IfxGtm_Pwm *pwm, IfxGtm_Pwm_Channel *channels, IfxGtm_Pwm_Config *config)
{
    uint8 idx = 0;

    /* 1. Change state to unknown */
    pwm->state = IfxGtm_Pwm_State_unknown;

    /* 2. Copy config data */
    pwm->gtmSFR            = config->gtmSFR;
    pwm->alignment         = config->alignment;
    pwm->syncUpdateEnabled = config->syncUpdateEnabled;
    pwm->subModule         = config->subModule;
    pwm->channels          = channels;
    pwm->cluster           = config->cluster;
    pwm->numChannels       = 0u;
    pwm->frequency         = config->frequency;
    pwm->clockSource       = config->clockSource;
    pwm->dtmClockSource    = config->dtmClockSource;

    if (pwm->subModule == IfxGtm_Pwm_SubModule_atom)
    {
        pwm->clusterSFR.ATOM = &config->gtmSFR->ATOM[(uint32)config->cluster];
    }
    else
    {
        pwm->clusterSFR.TOM = &config->gtmSFR->TOM[(uint32)config->cluster];
    }

    if ((uint32)config->cluster < IFXGTM_NUM_CDTM_OBJECTS)
    {
        pwm->clusterSFR.CDTM = &config->gtmSFR->CDTM[(uint32)config->cluster];
    }

    /* 3. Get Clock information */
    pwm->sourceFrequency = IfxGtm_Pwm_getSubModuleClockFrequency(pwm, config->gtmSFR, config->clockSource);
    pwm->dtmFrequency    = IfxGtm_Pwm_getDtmClockFrequency(pwm, config->gtmSFR, pwm->dtmClockSource);

    /* 4. Perform software reset of PWM channels */
    idx = (uint8)config->channels[0].timerCh;

    while (idx <= (uint8)config->channels[config->numChannels - 1u].timerCh)   /* Reset all channels in range even if not configured by user */
    {
        if (pwm->subModule == IfxGtm_Pwm_SubModule_atom)
        {
            pwm->clusterSFR.ATOM->AGC.GLB_CTRL.U = 1u << (IFX_GTM_ATOM_AGC_GLB_CTRL_RST_CH0_OFF + idx);
        }
        else
        {
            if (IFXGTM_PWM_GET_TGC_INDEX(idx) == 0)
            {
                pwm->clusterSFR.TOM->TGC0_GLB_CTRL.U = 1u << (IFX_GTM_TOM_TGC_GLB_CTRL_RST_CH0_OFF + (idx & 0x7u));
            }
            else
            {
                pwm->clusterSFR.TOM->TGC1_GLB_CTRL.U = 1u << (IFX_GTM_TOM_TGC_GLB_CTRL_RST_CH0_OFF + (idx & 0x7u));
            }
        }

        idx++;
    }

    /* 5. Initialize PWM channels */
    for (idx = 0; idx < config->numChannels; idx++)
    {
        IfxGtm_Pwm_initChannel(pwm, config, (IfxGtm_Pwm_SyncChannelIndex)idx);

        /* Update handle */
        pwm->numChannels += 1;
    }

    /* 6. Update masks and pointers to GLB_CTRL registers */
    IfxGtm_Pwm_updateGlobalControlUnitData(pwm);

    /* 7. Change state */
    pwm->state = IfxGtm_Pwm_State_init;

    /* 8. Synchronously start the channels */
    if (config->syncStart == TRUE)
    {
        IfxGtm_Pwm_startSyncedChannels(pwm);
    }
}


IFX_STATIC float32 IfxGtm_Pwm_getSubModuleClockFrequency(IfxGtm_Pwm *pwm, Ifx_GTM *gtmSFR, IfxGtm_Pwm_ClockSource clockSource)
{
    float32 frequency = 0.0f;

    /* Get PWM channel frequency */
    if (pwm->subModule == IfxGtm_Pwm_SubModule_atom)
    {
        frequency = IfxGtm_Cmu_getClkFrequency(gtmSFR, clockSource.atom, FALSE);
    }
    else    /* TOM */
    {
        frequency = IfxGtm_Cmu_getFxClkFrequency(gtmSFR, clockSource.tom, FALSE);
    }

    return frequency;
}


IFX_STATIC float32 IfxGtm_Pwm_getDtmClockFrequency(IfxGtm_Pwm *pwm, Ifx_GTM *gtmSFR, IfxGtm_Dtm_ClockSource clockSource)
{
    float32 dtmFrequency = 0.0f;

    /* Get DTM clock frequency */
    switch (clockSource)
    {
    case IfxGtm_Dtm_ClockSource_systemClock:    /* SYS_CLK selected */
    {
        dtmFrequency = IfxGtm_Cmu_getModuleFrequency(gtmSFR);
        break;
    }
    case IfxGtm_Dtm_ClockSource_cmuClock0:      /* CMU_CLK0 selected */
    {
        dtmFrequency = IfxGtm_Cmu_getClkFrequency(gtmSFR, IfxGtm_Cmu_Clk_0, FALSE);
        break;
    }
    case IfxGtm_Dtm_ClockSource_cmuClock1:
    {
        if (pwm->subModule == IfxGtm_Pwm_SubModule_atom)    /* CMU_CLK1 selected (if DTM is connected to an ATOM) */
        {
            dtmFrequency = IfxGtm_Cmu_getClkFrequency(gtmSFR, IfxGtm_Cmu_Clk_1, FALSE);
        }
        else                                                /* CMU_FXCLK0 selected (if DTM is connected to a TOM) */
        {
            dtmFrequency = IfxGtm_Cmu_getFxClkFrequency(gtmSFR, IfxGtm_Cmu_Fxclk_0, FALSE);
        }

        break;
    }
    case IfxGtm_Dtm_ClockSource_cmuClock2:
    {
        if (pwm->subModule == IfxGtm_Pwm_SubModule_atom)    /* CMU_CLK2 selected (if DTM is connected to an ATOM) */
        {
            dtmFrequency = IfxGtm_Cmu_getClkFrequency(gtmSFR, IfxGtm_Cmu_Clk_2, FALSE);
        }
        else                                                /* CMU_FXCLK1 selected (if DTM is connected to a TOM) */
        {
            dtmFrequency = IfxGtm_Cmu_getFxClkFrequency(gtmSFR, IfxGtm_Cmu_Fxclk_1, FALSE);
        }

        break;
    }
    default:
        /* do nothing */
        break;
    }

    return dtmFrequency;
}


IFX_STATIC void IfxGtm_Pwm_initChannel(IfxGtm_Pwm *pwm, IfxGtm_Pwm_Config *config, IfxGtm_Pwm_SyncChannelIndex configIndex)
{
    boolean                   baseChannel   = (configIndex == IfxGtm_Pwm_SyncChannelIndex_0) ? TRUE : FALSE;
    IfxGtm_Pwm_ChannelConfig *channelConfig = &config->channels[configIndex];
    IfxGtm_Pwm_SubModule      subModule     = pwm->subModule;
    IfxGtm_Pwm_ClusterSFR    *clusterSFR    = &pwm->clusterSFR;
    IfxGtm_Pwm_Channel       *channel       = &pwm->channels[configIndex];
    IfxGtm_Pwm_SubModule_Ch   channelIndex  = channelConfig->timerCh;

    channel->timerCh = channelIndex;

    /* 0. Store pointers to registers for faster access later */
    {
        channel->upenMask = (uint32)((uint32)IFXGTM_ATOM_AGC_CHANNEL_MASK << IFXGTM_ATOM_AGC_CHANNEL_SHIFT(channelIndex, IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL0_OFF));

        if (subModule == IfxGtm_Pwm_SubModule_atom)
        {
            Ifx_GTM_ATOM_CH *atomCh = IfxGtm_Atom_Ch_getChannelPointer(clusterSFR->ATOM, (IfxGtm_Atom_Ch)channelIndex);
            channel->registers.SR0        = (volatile uint32 *)(volatile void *)&atomCh->SR0.U;
            channel->registers.SR1        = (volatile uint32 *)(volatile void *)&atomCh->SR1.U;
            channel->registers.CM0        = (volatile uint32 *)(volatile void *)&atomCh->CM0.U;
            channel->registers.CM1        = (volatile uint32 *)(volatile void *)&atomCh->CM1.U;
            channel->registers.CN0        = (volatile uint32 *)(volatile void *)&atomCh->CN0.U;
            channel->registers.CTRL       = (volatile uint32 *)(volatile void *)&atomCh->CTRL.U;
            channel->registers.IRQ_NOTIFY = (volatile uint32 *)(volatile void *)&atomCh->IRQ.NOTIFY.U;
            channel->registers.GLB_CTRL   = (volatile uint32 *)(volatile void *)&clusterSFR->ATOM->AGC.GLB_CTRL.U;
        }
        else
        {
            Ifx_GTM_TOM_CH  *tomCh  = IfxGtm_Tom_Ch_getChannelPointer(clusterSFR->TOM, (IfxGtm_Tom_Ch)channelIndex);
            Ifx_GTM_TOM_TGC *tomTgc = IfxGtm_Tom_Ch_getTgcPointer(clusterSFR->TOM, IFXGTM_PWM_GET_TGC_INDEX(channelIndex));
            channel->registers.SR0        = (volatile uint32 *)(volatile void *)&tomCh->SR0.U;
            channel->registers.SR1        = (volatile uint32 *)(volatile void *)&tomCh->SR1.U;
            channel->registers.CM0        = (volatile uint32 *)(volatile void *)&tomCh->CM0.U;
            channel->registers.CM1        = (volatile uint32 *)(volatile void *)&tomCh->CM1.U;
            channel->registers.CN0        = (volatile uint32 *)(volatile void *)&tomCh->CN0.U;
            channel->registers.CTRL       = (volatile uint32 *)(volatile void *)&tomCh->CTRL.U;
            channel->registers.IRQ_NOTIFY = (volatile uint32 *)(volatile void *)&tomCh->IRQ.NOTIFY.U;
            channel->registers.GLB_CTRL   = (volatile uint32 *)(volatile void *)&tomTgc->GLB_CTRL.U;
        }
    }

    /* 1. Configure global control unit */
    {
        /* 1.1 Enable update of CM0/1 from shadow registers */
        if (pwm->syncUpdateEnabled == TRUE)
        {
            IfxGtm_Pwm_enableChannelSyncUpdate(channel->registers.GLB_CTRL, channel->upenMask);
        }
        else
        {
            IfxGtm_Pwm_disableChannelSyncUpdate(channel->registers.GLB_CTRL, channel->upenMask);
        }

        /* 1.2 Set channel to start counter when trigger is received */
        IfxGtm_Pwm_enableChannel(clusterSFR, subModule, channelIndex);

        /* 1.3 Set channel to start PWM output when trigger is received */
        IfxGtm_Pwm_enableChannelOutput(clusterSFR, subModule, channelIndex);
    }

    /* 2. Configure TOM/ATOM channel */
    Ifx_ActiveState channelPolarity = (channelConfig->output != NULL_PTR) ? channelConfig->output->polarity : Ifx_ActiveState_high;
    {
        /* 2.1 Set channel clock source */
        IfxGtm_Pwm_setClockSource(clusterSFR, subModule, channelIndex, pwm->clockSource);

        /* 2.2 Set Signal Polarity value here */
        IfxGtm_Pwm_setChannelPolarity(clusterSFR, subModule, channelIndex, channelPolarity);

        if (subModule == IfxGtm_Pwm_SubModule_atom)
        {
            /* 2.3 Set ATOM signal output mode as PWM */
            IfxGtm_Atom_Ch_setMode(clusterSFR->ATOM, (IfxGtm_Atom_Ch)channelIndex, IfxGtm_Atom_Mode_outputPwm);
        }

        /* 2.4 If base channel then generate trigger when CN0 matches CM0. This trigger will be used by sync channels
         * else if sync channel then disable trigger (forward from last channel) */
        IfxGtm_Pwm_generateTrigger(clusterSFR, subModule, channelIndex, baseChannel);
    }

    /* 3. Perform counter related configuration */
    {
        /* 3.1 Reset counter to 0 */
        *channel->registers.CN0 = 0x0u;

        /* 3.2 If base channel then reset counter value on CM0 match
         * else reset counter value on trigger from base channel */
        IfxGtm_Pwm_ResetEvent resetEvent = (baseChannel == TRUE) ? IfxGtm_Pwm_ResetEvent_onCm0 : IfxGtm_Pwm_ResetEvent_onTrigger;
        IfxGtm_Pwm_setCounterResetEvent(clusterSFR, subModule, channelIndex, resetEvent);

        /* 3.3 Set counter mode according to wave alignment */
        IfxGtm_Pwm_setCounterMode(clusterSFR, subModule, channelIndex, pwm->alignment);
    }

    /* 4. Calculate and set compare value registers */
    {
        /* 4.1 Calculate and set channel PWM frequency. Frequency check already performed */
        if (baseChannel == TRUE)
        {
            IfxGtm_Pwm_updateBaseChannelFrequency(pwm, pwm->frequency);
            channel->phaseTicks     = 0x0u;
            *channel->registers.CM0 = pwm->periodTicks;
        }
        /* 4.2 Calculate and set channel PWM phase shift for sync channels */
        else if (pwm->alignment == IfxGtm_Pwm_Alignment_edge)
        {
            IfxGtm_Pwm_updateChannelPhase(pwm, configIndex, channelConfig->phase);
            *channel->registers.CM0 = channel->phaseTicks;
        }
        else
        {}

        /* 4.3 Calculate and set channel PWM Duty */
        IfxGtm_Pwm_updateChannelDuty(pwm, configIndex, channelConfig->duty);
        *channel->registers.CM1 = channel->dutyTicks;
    }

    /* 5. Configure Dead time module */
    {
        IfxGtm_Dtm_Ch     dtmChannel = (IfxGtm_Dtm_Ch)((uint8)((uint8)channelIndex & 0x3u));
        Ifx_GTM_CDTM_DTM *dtmSFR     = IfxGtm_Pwm_getDtmPtr(&pwm->clusterSFR, subModule, channelIndex);

        if (channelConfig->dtm != NULL_PTR)
        {
            /* 5.1 Copy SFR to handle for later use */
            channel->registers.DTV = (volatile uint32 *)(volatile void *)&dtmSFR->CH[dtmChannel].DTV.U;

            /* 5.2 Set DTM Clock source */
            IfxGtm_Dtm_setClockSource(dtmSFR, pwm->dtmClockSource);

            /* 5.3 Enable dead time insertion at OUTx and OUTx_N */
            IfxGtm_Dtm_setOutput0DeadTimePath(dtmSFR, dtmChannel, IfxGtm_Dtm_DeadTimePath_enable);
            IfxGtm_Dtm_setOutput1DeadTimePath(dtmSFR, dtmChannel, IfxGtm_Dtm_DeadTimePath_enable);

            /* 5.4 Invert the signal at OUTx_N */
            IfxGtm_Dtm_setOutput1Select(dtmSFR, dtmChannel, IfxGtm_Dtm_Output1Select_inverseDeadTime);

            IfxGtm_Pwm_updateChannelDeadTimeImmediate(pwm, configIndex, channelConfig->dtm->deadTime);

            /* 5.7 Set complementary pin output polarity
             * Note: By default output polarity of DTM is inverse of original output
             * If same polarity is needed then invert the DTM output
             */
            boolean outputPolarity = ((channelConfig->output != NULL_PTR) && (channelConfig->output->polarity == channelConfig->output->complementaryPolarity)) ? TRUE : FALSE;
            IfxGtm_Dtm_setOutput1Polarity(dtmSFR, dtmChannel, (IfxGtm_Dtm_OutputPolarity)outputPolarity);
        }
        /* Set complementary pin output polarity if Dead time path is not being used. */
        else if ((channelConfig->output != NULL_PTR) && (channelConfig->output->complementaryPin != NULL_PTR))
        {
            /* Invert output if both are not equal */
            boolean outputPolarity1 = ((channelConfig->output->polarity != channelConfig->output->complementaryPolarity)) ? TRUE : FALSE;
            IfxGtm_Dtm_setOutput1Polarity(dtmSFR, dtmChannel, (IfxGtm_Dtm_OutputPolarity)(outputPolarity1));
        }
        else
        {}
    }

    /* 6. Initialize Output */
    if (channelConfig->output != NULL_PTR)
    {
        IfxGtm_Pwm_Pin pin;
        pin.outputMode = channelConfig->output->outputMode;
        pin.padDriver  = channelConfig->output->padDriver;
        pin.outputPin  = channelConfig->output->pin;

        /* Check pin availability and configure output pin */
        if (channelConfig->output->pin != NULL_PTR)
        {
            IfxGtm_Pwm_initPin(pwm, &pin);
        }

        /* Check pin availability and configure complementary output pin */
        if (channelConfig->output->complementaryPin != NULL_PTR)
        {
            pin.outputPin = channelConfig->output->complementaryPin;
            IfxGtm_Pwm_initPin(pwm, &pin);
        }
    }

    /* 7. Connect output to MSC */
    if (channelConfig->mscOut != NULL_PTR)
    {
        (void)IfxGtm_Trig_toMsc(pwm->gtmSFR, channelConfig->mscOut->signal, channelConfig->mscOut->mscSet, channelConfig->mscOut->source, channelConfig->mscOut->channel);
    }

    /* 8. Initialize Interrupts */
    IfxGtm_Pwm_initInterrupts(pwm, channelConfig->interrupt, channel);
}


IFX_STATIC void IfxGtm_Pwm_initInterrupts(IfxGtm_Pwm *pwm, IfxGtm_Pwm_InterruptConfig *interrupt, IfxGtm_Pwm_Channel *channel)
{
    volatile Ifx_SRC_SRCR  *src;
    IfxGtm_Pwm_SubModule_Ch channelIndex = channel->timerCh;

    if (interrupt != NULL_PTR)
    {
        boolean periodEventEnabled = (interrupt->periodEvent != NULL_PTR) ? TRUE : FALSE;
        boolean dutyEventEnabled   = (interrupt->dutyEvent != NULL_PTR) ? TRUE : FALSE;

        /* Copy callback function pointers */
        channel->periodEvent = interrupt->periodEvent;
        channel->dutyEvent   = interrupt->dutyEvent;

        /* Set notification and get src pointer */
        if (pwm->subModule == IfxGtm_Pwm_SubModule_atom)
        {
            IfxGtm_Atom_Ch_setNotification(pwm->clusterSFR.ATOM, (IfxGtm_Atom_Ch)channelIndex, interrupt->mode, periodEventEnabled, dutyEventEnabled);
            src = IfxGtm_Atom_Ch_getSrcPointer(NULL_PTR, (IfxGtm_Atom)pwm->cluster, (IfxGtm_Atom_Ch)channelIndex);
        }
        else
        {
            IfxGtm_Tom_Ch_setNotification(pwm->clusterSFR.TOM, (IfxGtm_Tom_Ch)channelIndex, interrupt->mode, periodEventEnabled, dutyEventEnabled);
            src = IfxGtm_Tom_Ch_getSrcPointer(NULL_PTR, (IfxGtm_Tom)pwm->cluster, (IfxGtm_Tom_Ch)channelIndex);
        }

        /* Initialize src node */
        IfxSrc_init(src, interrupt->isrProvider, interrupt->priority);

        /* Enable Src node */
        IfxSrc_enable(src);
    }
    else
    {
        /* Set notification and get src pointer */
        if (pwm->subModule == IfxGtm_Pwm_SubModule_atom)
        {
            IfxGtm_Atom_Ch_setNotification(pwm->clusterSFR.ATOM, (IfxGtm_Atom_Ch)channelIndex, IfxGtm_IrqMode_level, FALSE, FALSE);
        }
        else
        {
            IfxGtm_Tom_Ch_setNotification(pwm->clusterSFR.TOM, (IfxGtm_Tom_Ch)channelIndex, IfxGtm_IrqMode_level, FALSE, FALSE);
        }
    }
}


IFX_STATIC void IfxGtm_Pwm_updateGlobalControlUnitData(IfxGtm_Pwm *pwm)
{
    IfxGtm_Pwm_SubModule_Ch firstChannel = pwm->channels[0].timerCh;
    IfxGtm_Pwm_SubModule_Ch lastChannel  = pwm->channels[pwm->numChannels - 1u].timerCh;
    uint32                  mask         = 0;

    /* Initialize */
    pwm->globalControl.upenMask0     = 0u;
    pwm->globalControl.upenMask1     = 0u;
    pwm->globalControl.reg0          = NULL_PTR;
    pwm->globalControl.reg1          = NULL_PTR;
    pwm->globalControl.endisCtrlReg0 = NULL_PTR;
    pwm->globalControl.endisCtrlReg1 = NULL_PTR;

    /* Calculate Update enable mask for channels being used. Channels may not be consecutive. */
    {
        uint8 idx = 0;

        for (idx = 0; idx < pwm->numChannels; idx++)
        {
            mask |= 3u << (pwm->channels[idx].timerCh << 1u);
        }
    }

    if (pwm->subModule == IfxGtm_Pwm_SubModule_atom)
    {
        /* Get mask for channel range from first to last */
        pwm->globalControl.upenMask0     = (mask & 0xFFFFu) << (uint32)IFX_GTM_ATOM_AGC_GLB_CTRL_UPEN_CTRL0_OFF;
        pwm->globalControl.reg0          = (volatile uint32 *)(volatile void *)&pwm->clusterSFR.ATOM->AGC.GLB_CTRL.U;
        pwm->globalControl.endisCtrlReg0 = (volatile uint32 *)(volatile void *)&pwm->clusterSFR.ATOM->AGC.ENDIS_CTRL.U;
    }
    else
    {
        /* Enable both control units If channels are in different cluster */
        if (IFXGTM_PWM_GET_TGC_INDEX(firstChannel) != IFXGTM_PWM_GET_TGC_INDEX(lastChannel))
        {
            /* Get masks and values for both control units */
            pwm->globalControl.upenMask0     = (mask & 0xFFFFu) << (uint32)IFX_GTM_TOM_TGC_GLB_CTRL_UPEN_CTRL0_OFF;
            pwm->globalControl.upenMask1     = mask & 0xFFFF0000u;
            pwm->globalControl.reg0          = (volatile uint32 *)(volatile void *)&pwm->clusterSFR.TOM->TGC0_GLB_CTRL.U;
            pwm->globalControl.reg1          = (volatile uint32 *)(volatile void *)&pwm->clusterSFR.TOM->TGC1_GLB_CTRL.U;
            pwm->globalControl.endisCtrlReg0 = (volatile uint32 *)&pwm->clusterSFR.TOM->TGC0_ENDIS_CTRL.U;
            pwm->globalControl.endisCtrlReg1 = (volatile uint32 *)&pwm->clusterSFR.TOM->TGC1_ENDIS_CTRL.U;
        }
        else
        {
            pwm->globalControl.upenMask0 = (mask & 0xFFFFu) << (uint32)IFX_GTM_TOM_TGC_GLB_CTRL_UPEN_CTRL0_OFF;

            if (IFXGTM_PWM_GET_TGC_INDEX(lastChannel) == 0)
            {
                pwm->globalControl.reg0          = (volatile uint32 *)(volatile void *)&pwm->clusterSFR.TOM->TGC0_GLB_CTRL.U;
                pwm->globalControl.endisCtrlReg0 = (volatile uint32 *)(volatile void *)&pwm->clusterSFR.TOM->TGC0_ENDIS_CTRL.U;
            }
            else
            {
                pwm->globalControl.reg0          = (volatile uint32 *)(volatile void *)&pwm->clusterSFR.TOM->TGC1_GLB_CTRL.U;
                pwm->globalControl.endisCtrlReg0 = (volatile uint32 *)(volatile void *)&pwm->clusterSFR.TOM->TGC1_ENDIS_CTRL.U;
            }
        }
    }
}


void IfxGtm_Pwm_startSyncedChannels(IfxGtm_Pwm *pwm)
{
    Ifx_GTM_TBU *tbuSFR          = &pwm->gtmSFR->TBU;
    Ifx_GTM     *gtmSFR          = pwm->gtmSFR;
    boolean      cmuClockEnabled = FALSE;
    uint8        tbuClock;

    /* 1. Check if init is done */
    if ((pwm->state == IfxGtm_Pwm_State_init) ||
        (pwm->state == IfxGtm_Pwm_State_stopped))
    {
        /* 2. Use TBU to start different TGCs */
        if (pwm->globalControl.reg1 != NULL_PTR)
        {
            /* Enable channels on trigger */
            *pwm->globalControl.endisCtrlReg0 = (~(~IFXGTM_PWM_UPEN_ENABLE_VALUE & pwm->globalControl.upenMask0)) >> 16u;
            *pwm->globalControl.endisCtrlReg1 = (~(~IFXGTM_PWM_UPEN_ENABLE_VALUE & pwm->globalControl.upenMask1)) >> 16u;

            /* 2.1. Find which CMU_CLK is enabled and use the first one for TBU */
            for (tbuClock = (uint8)IfxGtm_Cmu_Clk_0; tbuClock < (uint8)IfxGtm_Cmu_Clk_7; tbuClock++)
            {
                if (IfxGtm_Cmu_isClkClockEnabled(gtmSFR, (IfxGtm_Cmu_Clk)tbuClock) == TRUE)
                {
                    cmuClockEnabled = TRUE;
                    break;
                }
            }

            /* 2.2 If no CMU_CLK is enabled then enable CMU_CLK0 */
            if (cmuClockEnabled == FALSE)
            {
                gtmSFR->CMU.CLK_EN.B.EN_CLK0 = (uint8)IfxGtm_FeatureControl_enable;
                tbuClock                     = (uint8)IfxGtm_Cmu_Clk_0;
            }

            /* 2.3 Disable TBU_TS0 for configuration */
            tbuSFR->CHEN.B.ENDIS_CH0 = (uint8)IfxGtm_FeatureControl_disable;

            /* 2.4 Configure AGC/TGC0/1 of pwm1 */
            IfxGtm_Pwm_configureTimeBaseTrigger(&pwm->clusterSFR, pwm->subModule, pwm->channels[0].timerCh, IFXGTM_PWM_TBU_TIMEOUT_TICKS);

            IfxGtm_Pwm_configureTimeBaseTrigger(&pwm->clusterSFR, pwm->subModule, IfxGtm_Pwm_SubModule_Ch_8, IFXGTM_PWM_TBU_TIMEOUT_TICKS);

            /* 2.5 TBU Set up */
            /* Configure clock source and base value */
            tbuSFR->CH0_CTRL.B.LOW_RES    = 0x0u;
            tbuSFR->CH0_CTRL.B.CH_CLK_SRC = (uint8)tbuClock;
            tbuSFR->CH0_BASE.U            = 0x0u;

            /* Start TBU_TS0 counter */
            tbuSFR->CHEN.B.ENDIS_CH0 = (uint8)IfxGtm_FeatureControl_enable;

            /* 2.6 Wait for trigger generation */
            while (tbuSFR->CH0_BASE.U < IFXGTM_PWM_TBU_TIMEOUT_TICKS)
            {}

            /* 2.7 Disable TBU_TS0 */
            tbuSFR->CHEN.B.ENDIS_CH0 = (uint8)IfxGtm_FeatureControl_disable;

            /* 2.8 Disable CM_CLK0 if enabled earlier */
            if (cmuClockEnabled == FALSE)
            {
                gtmSFR->CMU.CLK_EN.B.EN_CLK0 = (uint8)IfxGtm_FeatureControl_disable;
            }
        }
        /* 2. Give trigger to start counter of channels */
        else
        {
            /* Enable channels on trigger */
            *pwm->globalControl.endisCtrlReg0 = (~(~IFXGTM_PWM_UPEN_ENABLE_VALUE & pwm->globalControl.upenMask0)) >> 16u;
            *pwm->globalControl.reg0          = 0x1u;
        }

        /* 3. Update handle */
        pwm->state = IfxGtm_Pwm_State_run;
    }
}


void IfxGtm_Pwm_stopSyncedChannels(IfxGtm_Pwm *pwm)
{
    Ifx_GTM_TBU *tbuSFR          = &pwm->gtmSFR->TBU;
    Ifx_GTM     *gtmSFR          = pwm->gtmSFR;
    boolean      cmuClockEnabled = FALSE;
    uint8        tbuClock;

    /* 1. Check if PWM is running */
    if (pwm->state == IfxGtm_Pwm_State_run)
    {
        /* 2. Use TBU to stop different TGCs */
        if (pwm->globalControl.reg1 != NULL_PTR)
        {
            /* Disable channel on trigger */
            *pwm->globalControl.endisCtrlReg0 = (~(~IFXGTM_PWM_UPEN_DISABLE_VALUE & pwm->globalControl.upenMask0)) >> 16u;
            *pwm->globalControl.endisCtrlReg1 = (~(~IFXGTM_PWM_UPEN_DISABLE_VALUE & pwm->globalControl.upenMask1)) >> 16u;

            /* 2.1. Find which CMU_CLK is enabled and use the first one for TBU */
            for (tbuClock = IfxGtm_Cmu_Clk_0; tbuClock < IfxGtm_Cmu_Clk_7; tbuClock++)
            {
                if (IfxGtm_Cmu_isClkClockEnabled(gtmSFR, (IfxGtm_Cmu_Clk)tbuClock))
                {
                    cmuClockEnabled = TRUE;
                    break;
                }
            }

            /* 2.2 If no CMU_CLK is enabled then enable CMU_CLK0 */
            if (cmuClockEnabled == FALSE)
            {
                gtmSFR->CMU.CLK_EN.B.EN_CLK0 = IfxGtm_FeatureControl_enable;
                tbuClock                     = IfxGtm_Cmu_Clk_0;
            }

            /* 2.3 Disable TBU_TS0 for configuration */
            tbuSFR->CHEN.B.ENDIS_CH0 = IfxGtm_FeatureControl_disable;

            /* 2.4 Configure AGC/TGC0/1 of pwm1 */
            IfxGtm_Pwm_configureTimeBaseTrigger(&pwm->clusterSFR, pwm->subModule, pwm->channels[0].timerCh, IFXGTM_PWM_TBU_TIMEOUT_TICKS);

            IfxGtm_Pwm_configureTimeBaseTrigger(&pwm->clusterSFR, pwm->subModule, IfxGtm_Pwm_SubModule_Ch_8, IFXGTM_PWM_TBU_TIMEOUT_TICKS);

            /* 2.5 TBU Set up */
            /* Configure clock source and base value */
            tbuSFR->CH0_CTRL.B.LOW_RES    = 0x0u;
            tbuSFR->CH0_CTRL.B.CH_CLK_SRC = (uint32)tbuClock;
            tbuSFR->CH0_BASE.U            = 0x0u;

            /* Start TBU_TS0 counter */
            tbuSFR->CHEN.B.ENDIS_CH0 = IfxGtm_FeatureControl_enable;

            /* 2.6 Wait for trigger generation */
            while (tbuSFR->CH0_BASE.U < IFXGTM_PWM_TBU_TIMEOUT_TICKS)
            {}

            /* 2.7 Disable TBU_TS0 */
            tbuSFR->CHEN.B.ENDIS_CH0 = IfxGtm_FeatureControl_disable;

            /* 2.8 Disable CM_CLK0 if enabled earlier */
            if (cmuClockEnabled == FALSE)
            {
                gtmSFR->CMU.CLK_EN.B.EN_CLK0 = IfxGtm_FeatureControl_disable;
            }
        }
        /* 2. Give trigger to start counter of channels */
        else
        {
            /* Disable channel on trigger */
            *pwm->globalControl.endisCtrlReg0 = (~(~IFXGTM_PWM_UPEN_DISABLE_VALUE & pwm->globalControl.upenMask0)) >> 16u;
            *pwm->globalControl.reg0          = 0x1u;
        }

        /* 3. Update handle */
        pwm->state = IfxGtm_Pwm_State_stopped;
    }
}


void IfxGtm_Pwm_startSyncedGroups(IfxGtm_Pwm *pwm1, IfxGtm_Pwm *pwm2)
{
    Ifx_GTM_TBU *tbuSFR          = &pwm1->gtmSFR->TBU;
    Ifx_GTM     *gtmSFR          = pwm1->gtmSFR;
    boolean      cmuClockEnabled = FALSE;
    uint8        tbuClock;

    /* 1. Find which CMU_CLK is enabled and use the first one for TBU */
    for (tbuClock = (uint8)IfxGtm_Cmu_Clk_0; tbuClock < (uint8)IfxGtm_Cmu_Clk_7; tbuClock++)
    {
        if (IfxGtm_Cmu_isClkClockEnabled(gtmSFR, (IfxGtm_Cmu_Clk)tbuClock) == TRUE)
        {
            cmuClockEnabled = TRUE;
            break;
        }
    }

    /* 2. If no CMU_CLK is enabled then enable CMU_CLK0 */
    if (cmuClockEnabled == FALSE)
    {
        gtmSFR->CMU.CLK_EN.B.EN_CLK0 = (uint8)IfxGtm_FeatureControl_enable;
        tbuClock                     = (uint8)IfxGtm_Cmu_Clk_0;
    }

    /* 3. Disable TBU_TS0 for configuration */
    tbuSFR->CHEN.B.ENDIS_CH0 = (uint8)IfxGtm_FeatureControl_disable;

    /* 4. Configure AGC/TGC0/1 of pwm1 */
    *pwm1->globalControl.endisCtrlReg0 = (~(~IFXGTM_PWM_UPEN_ENABLE_VALUE & pwm1->globalControl.upenMask0)) >> 16u;
    IfxGtm_Pwm_configureTimeBaseTrigger(&pwm1->clusterSFR, pwm1->subModule, pwm1->channels[0].timerCh, IFXGTM_PWM_TBU_TIMEOUT_TICKS);

    if (pwm1->globalControl.reg1 != NULL_PTR)
    {
        *pwm1->globalControl.endisCtrlReg1 = (~(~IFXGTM_PWM_UPEN_ENABLE_VALUE & pwm1->globalControl.upenMask1)) >> 16u;
        IfxGtm_Pwm_configureTimeBaseTrigger(&pwm1->clusterSFR, pwm1->subModule, IfxGtm_Pwm_SubModule_Ch_8, IFXGTM_PWM_TBU_TIMEOUT_TICKS);
    }

    /* 5. Configure AGC/TGC0/1 of pwm1 */
    *pwm2->globalControl.endisCtrlReg0 = (~(~IFXGTM_PWM_UPEN_ENABLE_VALUE & pwm2->globalControl.upenMask0)) >> 16u;
    IfxGtm_Pwm_configureTimeBaseTrigger(&pwm2->clusterSFR, pwm2->subModule, pwm2->channels[0].timerCh, IFXGTM_PWM_TBU_TIMEOUT_TICKS);

    if (pwm2->globalControl.reg1 != NULL_PTR)
    {
        *pwm2->globalControl.endisCtrlReg1 = (~(~IFXGTM_PWM_UPEN_ENABLE_VALUE & pwm2->globalControl.upenMask1)) >> 16u;
        IfxGtm_Pwm_configureTimeBaseTrigger(&pwm2->clusterSFR, pwm2->subModule, IfxGtm_Pwm_SubModule_Ch_8, IFXGTM_PWM_TBU_TIMEOUT_TICKS);
    }

    /* 6. TBU Set up */
    /* 6.1 Configure clock source and base value */
    tbuSFR->CH0_CTRL.B.LOW_RES    = 0x0u;
    tbuSFR->CH0_CTRL.B.CH_CLK_SRC = (uint8)tbuClock;
    tbuSFR->CH0_BASE.U            = 0x0u;

    /* 6.2 Start TBU_TS0 counter */
    tbuSFR->CHEN.B.ENDIS_CH0 = (uint8)IfxGtm_FeatureControl_enable;

    /* 7. Wait for trigger generation */
    while (tbuSFR->CH0_BASE.U < IFXGTM_PWM_TBU_TIMEOUT_TICKS)
    {}

    /* 8. Disable TBU_TS0 */
    tbuSFR->CHEN.B.ENDIS_CH0 = (uint8)IfxGtm_FeatureControl_disable;

    /* 9. Disable CM_CLK0 if enabled earlier */
    if (cmuClockEnabled == FALSE)
    {
        gtmSFR->CMU.CLK_EN.B.EN_CLK0 = (uint8)IfxGtm_FeatureControl_disable;
    }

    pwm1->state = IfxGtm_Pwm_State_run;
    pwm2->state = IfxGtm_Pwm_State_run;
}


void IfxGtm_Pwm_stopSyncedGroups(IfxGtm_Pwm *pwm1, IfxGtm_Pwm *pwm2)
{
    Ifx_GTM_TBU *tbuSFR          = &pwm1->gtmSFR->TBU;
    Ifx_GTM     *gtmSFR          = pwm1->gtmSFR;
    boolean      cmuClockEnabled = FALSE;
    uint8        tbuClock;

    /* 1. Find which CMU_CLK is enabled and use the first one for TBU */
    for (tbuClock = (uint8)IfxGtm_Cmu_Clk_0; tbuClock < (uint8)IfxGtm_Cmu_Clk_7; tbuClock++)
    {
        if (IfxGtm_Cmu_isClkClockEnabled(gtmSFR, (IfxGtm_Cmu_Clk)tbuClock) == TRUE)
        {
            cmuClockEnabled = TRUE;
            break;
        }
    }

    /* 2. If no CMU_CLK is enabled then enable CMU_CLK0 */
    if (cmuClockEnabled == FALSE)
    {
        gtmSFR->CMU.CLK_EN.B.EN_CLK0 = (uint8)IfxGtm_FeatureControl_enable;
        tbuClock                     = (uint8)IfxGtm_Cmu_Clk_0;
    }

    /* 3. Disable TBU_TS0 for configuration */
    tbuSFR->CHEN.B.ENDIS_CH0 = (uint8)IfxGtm_FeatureControl_disable;

    /* 4. Configure AGC/TGC0/1 of pwm1 */
    *pwm1->globalControl.endisCtrlReg0 = (~(~IFXGTM_PWM_UPEN_DISABLE_VALUE & pwm1->globalControl.upenMask0)) >> 16u;
    IfxGtm_Pwm_configureTimeBaseTrigger(&pwm1->clusterSFR, pwm1->subModule, pwm1->channels[0].timerCh, IFXGTM_PWM_TBU_TIMEOUT_TICKS);

    if (pwm1->globalControl.reg1 != NULL_PTR)
    {
        *pwm1->globalControl.endisCtrlReg1 = (~(~IFXGTM_PWM_UPEN_DISABLE_VALUE & pwm1->globalControl.upenMask1)) >> 16u;
        IfxGtm_Pwm_configureTimeBaseTrigger(&pwm1->clusterSFR, pwm1->subModule, IfxGtm_Pwm_SubModule_Ch_8, IFXGTM_PWM_TBU_TIMEOUT_TICKS);
    }

    /* 5. Configure AGC/TGC0/1 of pwm1 */
    *pwm2->globalControl.endisCtrlReg0 = (~(~IFXGTM_PWM_UPEN_DISABLE_VALUE & pwm2->globalControl.upenMask0)) >> 16u;
    IfxGtm_Pwm_configureTimeBaseTrigger(&pwm2->clusterSFR, pwm2->subModule, pwm2->channels[0].timerCh, IFXGTM_PWM_TBU_TIMEOUT_TICKS);

    if (pwm2->globalControl.reg1 != NULL_PTR)
    {
        *pwm2->globalControl.endisCtrlReg1 = (~(~IFXGTM_PWM_UPEN_DISABLE_VALUE & pwm2->globalControl.upenMask1)) >> 16u;
        IfxGtm_Pwm_configureTimeBaseTrigger(&pwm2->clusterSFR, pwm2->subModule, IfxGtm_Pwm_SubModule_Ch_8, IFXGTM_PWM_TBU_TIMEOUT_TICKS);
    }

    /* 6. TBU Set up */
    /* 6.1 Configure clock source and base value */
    tbuSFR->CH0_CTRL.B.LOW_RES    = 0x0u;
    tbuSFR->CH0_CTRL.B.CH_CLK_SRC = (uint8)tbuClock;
    tbuSFR->CH0_BASE.U            = 0x0u;

    /* 6.2 Start TBU_TS0 counter */
    tbuSFR->CHEN.B.ENDIS_CH0 = (uint8)IfxGtm_FeatureControl_enable;

    /* 7. Wait for trigger generation */
    while (tbuSFR->CH0_BASE.U < IFXGTM_PWM_TBU_TIMEOUT_TICKS)
    {}

    /* 8. Disable TBU_TS0 */
    tbuSFR->CHEN.B.ENDIS_CH0 = (uint8)IfxGtm_FeatureControl_disable;

    /* 9. Disable CM_CLK0 if enabled earlier */
    if (cmuClockEnabled == FALSE)
    {
        gtmSFR->CMU.CLK_EN.B.EN_CLK0 = (uint8)IfxGtm_FeatureControl_disable;
    }

    pwm1->state = IfxGtm_Pwm_State_stopped;
    pwm2->state = IfxGtm_Pwm_State_stopped;
}


void IfxGtm_Pwm_updateFrequency(IfxGtm_Pwm *pwm, float32 requestFrequency)
{
    uint8               idx            = 0u;
    uint32              newDutyTicks   = 0u;
    uint32              newPhaseTicks  = 0u;
    float32             frequencyRatio = pwm->frequency / requestFrequency;
    IfxGtm_Pwm_Channel *channel;

    /* 1. Disable UPEN */
    IfxGtm_Pwm_disableChannelsSyncUpdate(pwm);

    /* 2. Calculate and set base channel PWM frequency */
    IfxGtm_Pwm_updateBaseChannelFrequency(pwm, requestFrequency);

    /* 3. Calculate and set base channel PWM duty */
    channel                 = &(pwm->channels[0]);
    newDutyTicks            = (uint32)((float32)(((float32)channel->dutyTicks * frequencyRatio) + (float32)0.5f));
    channel->dutyTicks      = newDutyTicks;
    *channel->registers.SR1 = (newDutyTicks <= pwm->periodTicks) ? newDutyTicks : newDutyTicks - pwm->periodTicks;

    /* 4. If edge aligned, calculate and set phase and duty of sync channels */
    if (pwm->alignment == IfxGtm_Pwm_Alignment_edge)
    {
        for (idx = 1; idx < pwm->numChannels; idx++)
        {
            channel                 = &(pwm->channels[idx]);

            newPhaseTicks           = (uint32)((float32)(((float32)channel->phaseTicks * frequencyRatio) + (float32)0.5f));
            newDutyTicks            = (uint32)((float32)(((float32)channel->dutyTicks * frequencyRatio) + (float32)0.5f));

            channel->phaseTicks     = newPhaseTicks;
            channel->dutyTicks      = newDutyTicks;

            *channel->registers.SR0 = newPhaseTicks;
            *channel->registers.SR1 = (newDutyTicks <= pwm->periodTicks) ? newDutyTicks : newDutyTicks - pwm->periodTicks;
        }
    }
    /* 5. If not edge aligned, only calculate and set duty of sync channels */
    else
    {
        for (idx = 1; idx < pwm->numChannels; idx++)
        {
            channel                 = &(pwm->channels[idx]);
            newDutyTicks            = (uint32)((float32)(((float32)channel->dutyTicks * frequencyRatio) + (float32)0.5f));
            channel->dutyTicks      = newDutyTicks;
            *channel->registers.SR1 = (newDutyTicks <= pwm->periodTicks) ? newDutyTicks : newDutyTicks - pwm->periodTicks;
        }
    }

    /* 6. Enable UPEN */
    IfxGtm_Pwm_enableChannelsSyncUpdate(pwm);
}


void IfxGtm_Pwm_updateSyncedGroupsFrequency(IfxGtm_Pwm *pwm1, IfxGtm_Pwm *pwm2, float32 requestFrequency)
{
    IfxGtm_Pwm         *pwmArray[2] = {pwm1, pwm2};
    IfxGtm_Pwm         *pwm;
    uint8               pwmIndex, idx;
    uint32              newDutyTicks;
    uint32              newPhaseTicks;
    float32             frequencyRatio;
    IfxGtm_Pwm_Channel *channel;

    /* 1. Disable UPEN */
    IfxGtm_Pwm_disableChannelsSyncUpdate(pwm1);
    IfxGtm_Pwm_disableChannelsSyncUpdate(pwm2);

    /* 2. Update frequency of two groups */
    for (pwmIndex = 0; pwmIndex < 2; pwmIndex++)
    {
        pwm            = pwmArray[pwmIndex];
        frequencyRatio = pwm->frequency / requestFrequency;

        /* Calculate and set base channel PWM frequency */
        IfxGtm_Pwm_updateBaseChannelFrequency(pwm, requestFrequency);

        /* Calculate and set base channel PWM duty */
        channel                 = &(pwm->channels[0]);
        newDutyTicks            = (uint32)((float32)(((float32)channel->dutyTicks * frequencyRatio) + (float32)0.5f));
        channel->dutyTicks      = newDutyTicks;
        *channel->registers.SR1 = (newDutyTicks <= pwm->periodTicks) ? newDutyTicks : newDutyTicks - pwm->periodTicks;

        /* If edge aligned, calculate and set phase and duty of sync channels */
        if (pwm->alignment == IfxGtm_Pwm_Alignment_edge)
        {
            for (idx = 1; idx < pwm->numChannels; idx++)
            {
                channel                 = &(pwm->channels[idx]);

                newPhaseTicks           = (uint32)((float32)(((float32)channel->phaseTicks * frequencyRatio) + (float32)0.5f));
                newDutyTicks            = (uint32)((float32)(((float32)channel->dutyTicks * frequencyRatio) + (float32)0.5f));

                channel->phaseTicks     = newPhaseTicks;
                channel->dutyTicks      = newDutyTicks;

                *channel->registers.SR0 = newPhaseTicks;
                *channel->registers.SR1 = (newDutyTicks <= pwm->periodTicks) ? newDutyTicks : newDutyTicks - pwm->periodTicks;
            }
        }
        /* If not edge aligned, only calculate and set duty of sync channels */
        else
        {
            for (idx = 1; idx < pwm->numChannels; idx++)
            {
                channel                 = &(pwm->channels[idx]);
                newDutyTicks            = (uint32)((float32)(((float32)channel->dutyTicks * frequencyRatio) + (float32)0.5f));
                channel->dutyTicks      = newDutyTicks;
                *channel->registers.SR1 = (newDutyTicks <= pwm->periodTicks) ? newDutyTicks : newDutyTicks - pwm->periodTicks;
            }
        }
    }

    /* 5. Enable UPEN */
    /* Update first control unit to ensure counters don't get out of sync */
    IfxGtm_Pwm_GlobalControl *globalctrl1 = &pwm1->globalControl;
    IfxGtm_Pwm_GlobalControl *globalctrl2 = &pwm2->globalControl;
    *globalctrl1->reg0 = pwm1->globalControl.upenMask0 & (uint32)IFXGTM_PWM_UPEN_ENABLE_VALUE;
    *globalctrl2->reg0 = pwm2->globalControl.upenMask0 & (uint32)IFXGTM_PWM_UPEN_ENABLE_VALUE;

    /* If CM0 of 1st channel ggot updated before 2nd channel, immediately update the second base channel's CM0 */
    if (((*pwm1->channels[0].registers.CM0) == pwm1->periodTicks) &&    /* CM0 has been updated from SR0 (frequency update already happened) */
        ((*pwm2->channels[0].registers.CM0) != pwm2->periodTicks))      /* CM0 not updated from SR0 (frequency update not happened yet) */
    {
        *pwm2->channels[0].registers.CM0 = pwm2->periodTicks;           /* Immediately update CM0 to prevent de-synchronization */
    }

    /* Update other TGC if present */
    if (pwm1->globalControl.reg1 != NULL_PTR)
    {
        *globalctrl1->reg1 = pwm1->globalControl.upenMask1 & (uint32)IFXGTM_PWM_UPEN_ENABLE_VALUE;
    }

    if (pwm2->globalControl.reg1 != NULL_PTR)
    {
        *globalctrl2->reg1 = pwm2->globalControl.upenMask1 & (uint32)IFXGTM_PWM_UPEN_ENABLE_VALUE;
    }
}


void IfxGtm_Pwm_updateFrequencyImmediate(IfxGtm_Pwm *pwm, float32 requestFrequency)
{
    uint8               idx            = 0u;
    uint32              newDutyTicks   = 0u;
    uint32              newPhaseTicks  = 0u;
    float32             frequencyRatio = pwm->frequency / requestFrequency;
    IfxGtm_Pwm_Channel *channel;

    /* 1. Calculate and set base channel PWM frequency */
    IfxGtm_Pwm_updateBaseChannelFrequencyImmediate(pwm, requestFrequency);

    /* 2. Calculate and set base channel PWM duty */
    channel                 = &(pwm->channels[0]);
    newDutyTicks            = (uint32)((float32)(((float32)channel->dutyTicks * frequencyRatio) + (float32)0.5f));
    channel->dutyTicks      = newDutyTicks;
    newDutyTicks            = (newDutyTicks <= pwm->periodTicks) ? newDutyTicks : newDutyTicks - pwm->periodTicks;

    *channel->registers.SR1 = newDutyTicks;
    *channel->registers.CM1 = newDutyTicks;

    /* 3. If edge aligned, calculate and set phase and duty of sync channels */
    if (pwm->alignment == IfxGtm_Pwm_Alignment_edge)
    {
        for (idx = 1; idx < pwm->numChannels; idx++)
        {
            channel                 = &(pwm->channels[idx]);

            newPhaseTicks           = (uint32)((float32)(((float32)channel->phaseTicks * frequencyRatio) + (float32)0.5f));
            newDutyTicks            = (uint32)((float32)(((float32)channel->dutyTicks * frequencyRatio) + (float32)0.5f));

            channel->phaseTicks     = newPhaseTicks;
            channel->dutyTicks      = newDutyTicks;

            newDutyTicks            = (newDutyTicks <= pwm->periodTicks) ? newDutyTicks : newDutyTicks - pwm->periodTicks;

            *channel->registers.SR0 = newPhaseTicks;
            *channel->registers.SR1 = newDutyTicks;
            *channel->registers.CM0 = newPhaseTicks;
            *channel->registers.CM1 = newDutyTicks;
        }
    }
    /* 4. If not edge aligned, only calculate and set duty of sync channels */
    else
    {
        for (idx = 1; idx < pwm->numChannels; idx++)
        {
            channel                 = &(pwm->channels[idx]);
            newDutyTicks            = (uint32)((float32)(((float32)channel->dutyTicks * frequencyRatio) + (float32)0.5f));
            channel->dutyTicks      = newDutyTicks;
            newDutyTicks            = (newDutyTicks <= pwm->periodTicks) ? newDutyTicks : newDutyTicks - pwm->periodTicks;
            *channel->registers.SR1 = newDutyTicks;
            *channel->registers.CM1 = newDutyTicks;
        }
    }
}


void IfxGtm_Pwm_updateChannelPulse(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SyncChannelIndex configIndex, float32 requestPhase, float32 requestDuty)
{
    /* 1. Disable update from shadow register */
    IfxGtm_Pwm_disableChannelSyncUpdate(pwm->channels[configIndex].registers.GLB_CTRL, pwm->channels[configIndex].upenMask);

    /* 2. Update phase if not base channel */
    if (configIndex != IfxGtm_Pwm_SyncChannelIndex_0)
    {
        IfxGtm_Pwm_updateChannelPhase(pwm, (IfxGtm_Pwm_SyncChannelIndex)configIndex, requestPhase);
    }

    /* 3. Update duty */
    IfxGtm_Pwm_updateChannelDuty(pwm, (IfxGtm_Pwm_SyncChannelIndex)configIndex, requestDuty);

    /* 4. Enable update from shadow register */
    IfxGtm_Pwm_enableChannelSyncUpdate(pwm->channels[configIndex].registers.GLB_CTRL, pwm->channels[configIndex].upenMask);
}


void IfxGtm_Pwm_updateChannelPulseImmediate(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SyncChannelIndex configIndex, float32 requestPhase, float32 requestDuty)
{
    /* 1. Update phase if not base channel */
    if (configIndex != IfxGtm_Pwm_SyncChannelIndex_0)
    {
        IfxGtm_Pwm_updateChannelPhaseImmediate(pwm, (IfxGtm_Pwm_SyncChannelIndex)configIndex, requestPhase);
    }

    /* 2. Update duty */
    IfxGtm_Pwm_updateChannelDutyImmediate(pwm, (IfxGtm_Pwm_SyncChannelIndex)configIndex, requestDuty);
}


void IfxGtm_Pwm_updateChannelsPhase(IfxGtm_Pwm *pwm, float32 *requestPhase)
{
    uint8 configIndex = 0;

    /* 1. Disable update from shadow register */
    IfxGtm_Pwm_disableChannelsSyncUpdate(pwm);

    /* 2. Loop over all channels and update duty */
    for (configIndex = 1; configIndex < pwm->numChannels; configIndex++)
    {
        IfxGtm_Pwm_updateChannelPhase(pwm, (IfxGtm_Pwm_SyncChannelIndex)configIndex, requestPhase[configIndex]);
    }

    /* 3. Enable update from shadow register */
    IfxGtm_Pwm_enableChannelsSyncUpdate(pwm);
}


void IfxGtm_Pwm_updateChannelsDuty(IfxGtm_Pwm *pwm, float32 *requestDuty)
{
    uint8 configIndex = 0;

    /* 1. Disable update from shadow register */
    IfxGtm_Pwm_disableChannelsSyncUpdate(pwm);

    /* 2. Loop over all channels and update duty */
    for (configIndex = 0; configIndex < pwm->numChannels; configIndex++)
    {
        IfxGtm_Pwm_updateChannelDuty(pwm, (IfxGtm_Pwm_SyncChannelIndex)configIndex, requestDuty[configIndex]);
    }

    /* 3. Enable update from shadow register */
    IfxGtm_Pwm_enableChannelsSyncUpdate(pwm);
}


void IfxGtm_Pwm_updateChannelsPulse(IfxGtm_Pwm *pwm, float32 *requestPhase, float32 *requestDuty)
{
    uint8 configIndex = 0;

    /* 1. Disable update from shadow register */
    IfxGtm_Pwm_disableChannelsSyncUpdate(pwm);

    /* 2. Update duty for base channel */
    IfxGtm_Pwm_updateChannelDuty(pwm, IfxGtm_Pwm_SyncChannelIndex_0, requestDuty[0]);

    /* 3. Loop over sync channels and update phase & duty */
    for (configIndex = 1; configIndex < pwm->numChannels; configIndex++)
    {
        IfxGtm_Pwm_updateChannelPhase(pwm, (IfxGtm_Pwm_SyncChannelIndex)configIndex, requestPhase[configIndex]);

        IfxGtm_Pwm_updateChannelDuty(pwm, (IfxGtm_Pwm_SyncChannelIndex)configIndex, requestDuty[configIndex]);
    }

    /* 4. Enable update from shadow register */
    IfxGtm_Pwm_enableChannelsSyncUpdate(pwm);
}


void IfxGtm_Pwm_updateChannelsDeadTimeImmediate(IfxGtm_Pwm *pwm, IfxGtm_Pwm_DeadTime *requestDeadTime)
{
    uint8 configIndex = 0;

    /* Loop over all channels and update dead time */
    for (configIndex = 0; configIndex < pwm->numChannels; configIndex++)
    {
        IfxGtm_Pwm_updateChannelDeadTimeImmediate(pwm, (IfxGtm_Pwm_SyncChannelIndex)configIndex, requestDeadTime[configIndex]);
    }
}


void IfxGtm_Pwm_updateChannelsPhaseImmediate(IfxGtm_Pwm *pwm, float32 *requestPhase)
{
    uint8 configIndex = 0;

    /* Loop over all channels and update duty */
    for (configIndex = 1; configIndex < pwm->numChannels; configIndex++)
    {
        IfxGtm_Pwm_updateChannelPhaseImmediate(pwm, (IfxGtm_Pwm_SyncChannelIndex)configIndex, requestPhase[configIndex]);
    }
}


void IfxGtm_Pwm_updateChannelsDutyImmediate(IfxGtm_Pwm *pwm, float32 *requestDuty)
{
    uint8 configIndex = 0;

    /* Loop over all channels and update duty */
    for (configIndex = 0; configIndex < pwm->numChannels; configIndex++)
    {
        IfxGtm_Pwm_updateChannelDutyImmediate(pwm, (IfxGtm_Pwm_SyncChannelIndex)configIndex, requestDuty[configIndex]);
    }
}


void IfxGtm_Pwm_updateChannelsPulseImmediate(IfxGtm_Pwm *pwm, float32 *requestPhase, float32 *requestDuty)
{
    uint8 configIndex = 0;

    /* 1. Update duty for base channel */
    IfxGtm_Pwm_updateChannelDutyImmediate(pwm, IfxGtm_Pwm_SyncChannelIndex_0, requestDuty[0]);

    /* 2. Loop over sync channels and update phase & duty */
    for (configIndex = 1; configIndex < pwm->numChannels; configIndex++)
    {
        IfxGtm_Pwm_updateChannelPhaseImmediate(pwm, (IfxGtm_Pwm_SyncChannelIndex)configIndex, requestPhase[configIndex]);

        IfxGtm_Pwm_updateChannelDutyImmediate(pwm, (IfxGtm_Pwm_SyncChannelIndex)configIndex, requestDuty[configIndex]);
    }
}


void IfxGtm_Pwm_interruptHandler(IfxGtm_Pwm_Channel *channel, void *data)
{
    /* Note: IRQ_NOTIFY is raised even for disabled IRQ_EN.CCUxTC_IRQ_EN
     * Therefore, check each bit individually
     */
    /* CCU0 interrupt handler */
    if ((*channel->registers.IRQ_NOTIFY & 0x1u) == 1u)
    {
        /* Call callback function */
        if (channel->periodEvent != NULL_PTR)
        {
            (channel->periodEvent)(data);
        }

        /* Clear only period interrupt */
        *channel->registers.IRQ_NOTIFY = 0x1u;
    }

    /* CCU1 interrupt handler */
    if ((*channel->registers.IRQ_NOTIFY & 0x2u) == 2u)
    {
        /* Call callback function */
        if (channel->dutyEvent != NULL_PTR)
        {
            (channel->dutyEvent)(data);
        }

        /* Clear only duty interrupt */
        *channel->registers.IRQ_NOTIFY = 0x2u;
    }
}


IfxGtm_Pwm_ChannelState IfxGtm_Pwm_getChannelState(IfxGtm_Pwm *pwm, IfxGtm_Pwm_SubModule_Ch channel)
{
    IfxGtm_Pwm_ChannelState state = IfxGtm_Pwm_ChannelState_stopped;
    uint32                  value;

    /* Channel enabled if ENDIS and OUTEN is enabled for a certain channel */
    if (pwm->subModule == IfxGtm_Pwm_SubModule_atom)
    {
        value = IFXGTM_ATOM_AGC_CHANNEL_MASK &
                ((pwm->clusterSFR.ATOM->AGC.ENDIS_STAT.U & pwm->clusterSFR.ATOM->AGC.OUTEN_STAT.U) >>
                 IFXGTM_ATOM_AGC_CHANNEL_SHIFT(channel, 0));
    }
    else
    {
        if (IFXGTM_PWM_GET_TGC_INDEX(channel) == 0)
        {
            value = IFXGTM_TOM_TGC_CHANNEL_MASK &
                    ((pwm->clusterSFR.TOM->TGC0_ENDIS_STAT.U & pwm->clusterSFR.TOM->TGC0_OUTEN_STAT.U) >>
                     IFXGTM_TOM_TGC_CHANNEL_SHIFT(channel, 0));
        }
        else
        {
            value = IFXGTM_TOM_TGC_CHANNEL_MASK &
                    ((pwm->clusterSFR.TOM->TGC1_ENDIS_STAT.U & pwm->clusterSFR.TOM->TGC1_OUTEN_STAT.U) >>
                     IFXGTM_TOM_TGC_CHANNEL_SHIFT(channel, 0));
        }
    }

    /* 0b11 means output and channel is enabled */
    if (value == 0x3u)
    {
        state = IfxGtm_Pwm_ChannelState_running;
    }

    return state;
}
